#ifndef VECTOR3TEM_H
#define VECTOR3TEM_H

#define SIZE 3

template <typename Real>
struct Vector3Tem;

#include "imMaths.h"
#include "Matrix4.h"

template <typename Real>
struct Vector3Tem {
public:
	union {
		struct {
			Real x, y, z;
		};
		Real Data[SIZE];
	};

	// Constructors
	Vector3Tem(void) {
		memset(&Data, 0, sizeof(Real) * SIZE);
	}
	Vector3Tem(const Real * f) {
		x = f[0];
		y = f[1];
		z = f[2];
	}
	Vector3Tem(Real _x, Real _y, Real _z) {
		x = _x;
		y = _y;
		z = _z;
	}

	// Members
	Vector3Tem<Real> Cross(const Vector3Tem<Real> &a) { // Returns Cross Product of this and a
		return Vector3Tem<Real>((y*a.z - z*a.y),
							 (z*a.x - x*a.z),
							 (x*a.y - y*a.x));
	}
	Vector3Tem<Real> ComponentProduct(const Vector3Tem<Real> &a) { // Multiply each component of the vector together
		return Vector3Tem<Real>(x * a.x,
							 y * a.y,
							 z * a.z);
	}
	Real Dot(const Vector3Tem<Real> &a) { // Dot product
		return x*a.x + y*a.y + z*a.z;
	}
	Real LengthSqr(void) { // Returns length^2 of vector. Note that this is faster than Length()
		return x*x + y*y + z*z;
	}
	Real Length(void) { // Returns length of vector
		return sqrt(LengthSqr());
	}
	void Normalize(void) { // Converts vector to unit length
		Real l = Length();
		x /= l;
		y /= l;
		z /= l;
	}
	void AddScaledVector(const Vector3Tem<Real> &a, const Real &s) {
		x += a.x * s;
		y += a.y * s;
		z += a.z * s;
	}
	
	// Operators! Woo!
	Real operator*(const Vector3Tem<Real> &a) { // Dot product
		return Dot(a);
	}
		
	Vector3Tem<Real> operator+(const Vector3Tem<Real> &a) {
		return Vector3Tem<Real> (x + a.x, y + a.y, z + a.z);
	}
	Vector3Tem<Real> operator-(const Vector3Tem<Real> &a) {
		return Vector3Tem<Real> (x - a.x, y - a.y, z - a.z);
	}
	Vector3Tem<Real> & operator+=(const Vector3Tem<Real> &a) {
		x += a.x;
		y += a.y;
		z += a.z;
		return *this;
	}
	Vector3Tem<Real> & operator-=(const Vector3Tem<Real> &a) {
		x -= a.x;
		y -= a.y;
		z -= a.z;
		return *this;
	}
		
	Vector3Tem<Real> operator*(const Real &a) {
		return Vector3Tem<Real> (x * a, y * a, z * a);
	}
	Vector3Tem<Real> operator/(const Real &a) {
		return Vector3Tem<Real> (x / a, y / a, z / a);
	}
	Vector3Tem<Real> & operator*=(const Real &a) {
		x *= a;
		y *= a;
		z *= a;
		return *this;
	}
	Vector3Tem<Real> & operator/=(const Real &a) {
		x /= a;
		y /= a;
		z /= a;
		return *this;
	}

	bool operator== (const Vector3Tem<Real> &v) {
		Vector3Tem<Real> p = this->operator-(v);
		return abs(p.x) < FLT_EPSILON && abs(p.y) < FLT_EPSILON && abs(p.z) < FLT_EPSILON;
	}

	operator const Vector3Tem<float>(void) {
		Vector3Tem<float> o;

		for(int i = 0; i < SIZE; i++)
			o.Data[i] = (float)Data[i];

		return o;
	}

	operator const Vector3Tem<double>(void) {
		Vector3Tem<double> o;

		for(int i = 0; i < SIZE; i++)
			o.Data[i] = (double)Data[i];

		return o;
	}
	
	Vector3Tem<Real> operator* (const Matrix4Tem<Real> &m) {
		Vector3Tem<Real> Out;

		for(int i = 0; i < SIZE; i++) {
			for(int j = 0; j < SIZE; j++) {
				Out.Data[i] += m.Data[i][j] * Data[j];
			}
		}

		for(int i = 0; i < 3; i++) {
			Out.Data[i] += m.Data[i][3];
		}

		return Out;
	}
};

#undef SIZE

typedef Vector3Tem<float> Vector;
typedef Vector3Tem<float> Vectorf;
typedef Vector3Tem<double> Vectord;

typedef Vector3Tem<float> Vector3;
typedef Vector3Tem<float> Vector3f;
typedef Vector3Tem<double> Vector3d;

#endif